#include<iostream>

#include<stdio.h>

#include<string>

#include <sys/types.h>

#include <sys/socket.h>

#include <netinet/in.h>

#include <arpa/inet.h>

#include<string.h>

#include<unistd.h>

#include<vector>

#include<unordered_map>

#include<signal.h>

#include"tp.hpp"

#include"Daemon.hpp"

#define SIZE 1024

std::string defaultip = "0.0.0.0";
uint16_t defaultport = 8080;

const int backlog = 5;

enum
{
    SOCKETERR = 1,
    BINDERR,
    lISTENERR,
    READERR
};

class tcpserver;

struct task
{
    int skfd;
    sockaddr_in in;
    tcpserver* THIS;

    void operator()(const std::string& name);
};

class tcpserver
{
public:
    // struct task
    // {
    //     int skfd;
    //     sockaddr_in in;
    //     tcpserver* THIS;

    //     void operator()(const std::string& name)
    //     {
    //         char buffer[SIZE] = {0};
    //         while(1)
    //         {
    //             ssize_t n = read(skfd, buffer, SIZE);
    //             if(n > 0)
    //             {
    //                 buffer[n] = 0;
    //                 std::string ip = inet_ntoa(in.sin_addr);
    //                 std::string message = "[" + ip + ":" + std::to_string((int)ntohs(in.sin_port)) + "]" + buffer;

    //                 std::cout << name << " : " << message << std::endl;
    //                 THIS->Broadcast(message);
    //             }
    //             else if(n == 0)
    //             {
    //                 std::cout << "user quit. " << name << " done" << std::endl;
    //                 break;
    //             }
    //             else
    //             {
    //                 perror("read");
    //                 continue;
    //             }
    //         }
    //     }
    // };

    tcpserver(const std::string& ip = defaultip, uint16_t port = defaultport):
        _ip(defaultip),
        _port(port),
        _sockfd(0),
        isrunning(false)
    {
    }

    void init()
    {
        Daemon("/");
        _sockfd = socket(AF_INET, SOCK_STREAM, 0);
        if(_sockfd < 0)
        {
            perror("socket");
            exit(SOCKETERR);
        }

        int opt = 1;
        setsockopt(_sockfd, SOL_SOCKET, SO_REUSEADDR|SO_REUSEPORT, &opt, sizeof(opt)); // 防止偶发性的服务器无法进行立即重启(tcp协议的时候再说)

        sockaddr_in in;
        bzero(&in, sizeof(in));
        in.sin_addr.s_addr = inet_addr(_ip.c_str());
        in.sin_family = AF_INET;
        in.sin_port = htons(_port);

        if(bind(_sockfd, (const sockaddr*)&in, sizeof(in)) < 0)
        {
            perror("bind");
            exit(BINDERR);
        }
        std::cout << "bind success" << std::endl;

        if(listen(_sockfd, backlog) < 0)
        {
            perror("listen");
            exit(lISTENERR);
        }

        std::cout << "listen success" << std::endl;

        _tp.start();

        std::cout << "thread_pool start success" << std::endl;
    }

    void Broadcast(const std::string& message)
    {
        for(auto& e : _um)
        {
            std::cout << 1 << std::endl;
            write(e.second->skfd, message.c_str(), message.size());
        }
    }

    static void* service(void* arg)
    {
        signal(SIGPIPE, SIG_IGN);
        pthread_detach(pthread_self());
        // std::cout << 1 << std::endl;
        task* tkptr = static_cast<task*>(arg);
        char buffer[SIZE] = {0};
        while(1)
        {
            ssize_t n = read(tkptr->skfd, buffer, SIZE);
            if(n > 0)
            {
                buffer[n] = 0;
                std::string ip = inet_ntoa(tkptr->in.sin_addr);
                std::string message = "[" + ip + ":" + std::to_string((int)ntohs(tkptr->in.sin_port)) + "]" + buffer;
                // std::cout << "[" << ip << ":" << tkptr->in.sin_port << "]" << buffer << std::endl;

                std::cout << message << std::endl;
                tkptr->THIS->Broadcast(message);
            }
        }

        delete tkptr;

        return nullptr;
    }

    void check_user(task*& tkptr)
    {
        std::string strid = inet_ntoa(tkptr->in.sin_addr);
        int intport = tkptr->in.sin_port;
        strid += ":" + std::to_string(intport);
        if(_um.count(strid) == 0)
        {
            _um[strid] = tkptr;
            std::cout << "add a new user：" << strid << std::endl;
        }
    }

    void run()
    {
        isrunning = true;
        char buffer[SIZE] = {0};
        std::string message;
        std::vector<pthread_t>add;
        while(isrunning)
        {
            task* tkptr = new task;
            tkptr->THIS = this;
            socklen_t inlen = sizeof(tkptr->in);
            tkptr->skfd = accept(_sockfd, (sockaddr*)&(tkptr->in), &inlen);

            check_user(tkptr);

            _tp.push(*tkptr);
        }
    }

    ~tcpserver()
    {
        close(_sockfd);
        for(auto& e : _um)
        {
            delete e.second;
        }
    }
private:
    std::string _ip;
    uint16_t _port;
    int _sockfd;
    bool isrunning;
    std::unordered_map<std::string, task*>_um;
    thread_pool<task> _tp;
};

void task::operator()(const std::string& name)
{
    char buffer[SIZE] = {0};
    while(1)
    {
        ssize_t n = read(skfd, buffer, SIZE);
        if(n > 0)
        {
            buffer[n] = 0;
            std::string ip = inet_ntoa(in.sin_addr);
            std::string message = "[" + ip + ":" + std::to_string((int)ntohs(in.sin_port)) + "]" + buffer;

            std::cout << name << " : " << message << std::endl;
            THIS->Broadcast(message);
        }
        else if(n == 0)
        {
            std::cout << "user quit. " << name << " done" << std::endl;
            break;
        }
        else
        {
            perror("read");
            continue;
        }
    }
}